import { Vec3 } from 'cc';
import { Point } from './ts-geometry-2d/Point';
import { TableAtrribute } from '../core/TableAttribute';
import { DeskBall, DeskHole, AiGTool, FreeKickPosition } from './aicore';
import { HitPathGenerate } from './HitPathGenerate';
import { HitSetSpin } from './HitSetSpin';
import { AiLevelController } from './AiLevelController';

// AI击球思考的更像人类
export class HumanAiBrain {
  // 球桌距离最大值的平方, 斜对角线的最大值
  public static MaxDistanceSquare = 2000;
  public radius = 0.5;
  private aiLevel = 0; // L1 L2 L3 L4 L5
  constructor(lv: number = 0) { this.aiLevel = lv; }
  public updateAiLevel(lv: number) { this.aiLevel = lv; }
  // 选择默认可以击打的球【用于计算AI或者用户开始击球时默认瞄准的球】
  public generateBestHitPosition(mainBall: DeskBall, attackBalles: Array<DeskBall>,
    protectBalles: Array<DeskBall>, deskHoles: Array<DeskHole>) {
    if (attackBalles.length <= 0) {
      return new Vec3(0, TableAtrribute.DeskTopAxisY, 0);
    }
    if (attackBalles.length === 1) {
      return new Vec3(attackBalles[0].position.x, TableAtrribute.DeskTopAxisY, attackBalles[0].position.y);
    }
    var maxdist = HumanAiBrain.MaxDistanceSquare * HumanAiBrain.MaxDistanceSquare;
    var minpos = attackBalles[0].position;
    // 计算 白球到目标球，目标球到球洞的距离 作为衡量因子
    for (const ball of attackBalles) {
      const balls = attackBalles.filter(b => b.index !== ball.index);
      const allBalls = balls.concat(protectBalles);
      if (AiGTool.hasOccluded(mainBall.position, ball.position, allBalls)) {
        continue;
      }
      const wdist = mainBall.position.distanceSquare(ball.position);
      for (const hole of deskHoles) {
        const tdist = Math.sqrt(ball.position.distanceSquare(hole.position));
        var dist = wdist * tdist;
        // 有遮挡，则给予惩罚, 惩罚因子 100
        if (AiGTool.hasOccluded(ball.position, hole.position, allBalls)) {
          dist = dist / 100;
        }
        if (dist < maxdist) {
          maxdist = dist;
          minpos = ball.position;
        }
      }
    }
    return new Vec3(minpos.x, TableAtrribute.DeskTopAxisY, minpos.y);
  }

  // 计算自由球摆放的所有候选位置
  public generateFreeKickBallPosition(attackBalles: Array<DeskBall>,
    protectBalles: Array<DeskBall>, deskHoles: Array<DeskHole>) {
    const candidates = new Array<FreeKickPosition>();
    if (attackBalles.length <= 0) {
      candidates.push(new FreeKickPosition(new Point(0, 0), new Point(Math.random() * 10, Math.random() * 10), deskHoles[0]));
      return candidates;
    }
    const aLen = attackBalles.length;
    for (const ball of attackBalles) {
      const balls = attackBalles.filter(b => b.index !== ball.index);
      // 排除自己以外的所有球，都可能形成遮挡
      const allBalls = balls.concat(protectBalles);
      const dl = 2;
      for (const hole of deskHoles) {
        // 是否被遮挡
        if (AiGTool.hasOccluded(ball.position, hole.position, allBalls)) {
          continue;
        }
        const dirv = ball.position.minus(hole.position);
        const sc = dl + Math.random() * 4;
        //【1】沿瞄准方向后退一点距离即为白球的最佳候选位置
        const mainPos = ball.position.plus(dirv.normed().scale(sc));
        // x,y 需要一点偏差
        mainPos.x = mainPos.x + (Math.random() - 0.5);
        mainPos.y = mainPos.y + (Math.random() - 0.5);
        const score = 1;
        if (!AiGTool.hasOccluded(mainPos, ball.position, allBalls)) {
          const fkp = new FreeKickPosition(mainPos, ball.position, hole);
          fkp.updateScore(10 * score);
          candidates.push(fkp);
        }
      }
    }
    if (candidates.length < 3) {
      const randomCands = this.randomFreeKickBall(attackBalles, protectBalles, deskHoles);
      candidates.concat(randomCands);
    }
    // 候选位置加随机数会导致出界，先作一些修正，然后在计算分值
    for (const p of candidates) {
      if (p.pos.x > TableAtrribute.DeskHalfAxisXLength - 0.51) {
        p.pos.x = TableAtrribute.DeskHalfAxisXLength - 0.51;
      }
      if (p.pos.x < -TableAtrribute.DeskHalfAxisXLength + 0.51) {
        p.pos.x =  -TableAtrribute.DeskHalfAxisXLength + 0.51;
      }
      if (p.pos.y > TableAtrribute.DeskHalfAxisZLength - 0.51) {
        p.pos.y = TableAtrribute.DeskHalfAxisZLength - 0.51;
      }
      if (p.pos.y < -TableAtrribute.DeskHalfAxisZLength + 0.51) {
        p.pos.y = -TableAtrribute.DeskHalfAxisZLength + 0.51;
      }
      p.score = p.score * this.calcuFreeMainTargetHoleScore(p.pos, p.targetPos, p.hole);
    }
    candidates.sort((a, b) => b.score - a.score);
    // 最后如果没有候选的话，在加入几个随机位置兜底
    if (candidates.length <= 0) {
      candidates.push(new FreeKickPosition(new Point(0, 0), new Point(Math.random() * 10, Math.random() * 5), deskHoles[0]));
      candidates.push(new FreeKickPosition(new Point(0, 0), new Point(Math.random() * 10, Math.random() * 5), deskHoles[1]));
      candidates.push(new FreeKickPosition(new Point(0, 0), new Point(Math.random() * 10, Math.random() * 5), deskHoles[2]));
    }
    console.log('free candidates ', candidates);
    return candidates;
  }

  // 生成所有的最好击球路径
  public generateAllBestPath(mainBall: DeskBall, attackBalles: Array<DeskBall>,
    protectBalles: Array<DeskBall>, deskHoles: Array<DeskHole>) {
    const hitGen = new HitPathGenerate();
    
    console.log('ai brain level ', this.aiLevel, deskHoles);
    const paths = hitGen.generateAllBestHitPath(mainBall, attackBalles, protectBalles, deskHoles);
    new HitSetSpin(this.aiLevel).hitSpin(mainBall, paths);
    const allBalls = [...attackBalles, ...protectBalles];
    new AiLevelController(this.aiLevel).aiLevelController(mainBall, paths, allBalls);
    return paths;
  }

  // 只生成最好击球路径【仅用于快速决策自由球时最好的位置】
  public generateOnlyBestPath(mainBall: DeskBall, attackBalles: Array<DeskBall>,
    protectBalles: Array<DeskBall>, deskHoles: Array<DeskHole>) {
    const hitGen = new HitPathGenerate();
    return hitGen.generateOnlyDirectBestPath(mainBall, attackBalles, protectBalles, deskHoles);
  }

  // 计算白球自由球位置的分值 白球 -> 目标球 -> 球洞 =》分值
  private calcuFreeMainTargetHoleScore(mpos: Point, targetPos: Point, hole: DeskHole) {
    var distth = targetPos.distanceSquare(hole.position);
    if (distth > HumanAiBrain.MaxDistanceSquare) distth = HumanAiBrain.MaxDistanceSquare;
    const dira = targetPos.minus(mpos).normed();
    const dirb = hole.position.minus(targetPos).normed();
    const costheta = dira.dot(dirb); // [0度，90度]

    return costheta * (1 - distth / HumanAiBrain.MaxDistanceSquare);
  }

  private randomFreeKickBall(attackBalles: Array<DeskBall>, protectBalles: Array<DeskBall>, deskHoles: Array<DeskHole>) {
    const candidates = new Array<FreeKickPosition>();
    for (const ball of attackBalles) {
      const balls = attackBalles.filter(b => b.index !== ball.index);
      // 排除自己以外的所有球，都可能形成遮挡
      const allBalls = balls.concat(protectBalles);
      const dl = 3
      for (const hole of deskHoles) {
        //【2】目标球四面八方都选择一个位置，加入到白球的候选位置【尤其是只剩下一个球时，可以多尝试一些位置更合理】
        const mainPos1 = new Point(ball.position.x + dl + Math.random() * 4, ball.position.y);
        if (!AiGTool.hasOccluded(mainPos1, ball.position, allBalls)) {
          candidates.push(new FreeKickPosition(mainPos1, ball.position, hole));
        }
        const mainPos2 = new Point(ball.position.x - dl - Math.random() * 4, ball.position.y);
        if (!AiGTool.hasOccluded(mainPos2, ball.position, allBalls)) {
          candidates.push(new FreeKickPosition(mainPos2, ball.position, hole));
        }
        const mainPos3 = new Point(ball.position.x, ball.position.y + dl + Math.random() * 4);
        if (!AiGTool.hasOccluded(mainPos3, ball.position, allBalls)) {
          candidates.push(new FreeKickPosition(mainPos3, ball.position, hole));
        }
        const mainPos4 = new Point(ball.position.x, ball.position.y - dl - Math.random() * 4);
        if (!AiGTool.hasOccluded(mainPos4, ball.position, allBalls)) {
          candidates.push(new FreeKickPosition(mainPos4, ball.position, hole));
        }
        const mainPos5 = new Point(ball.position.x + dl + Math.random() * 4, ball.position.y + dl + Math.random() * 4);
        if (!AiGTool.hasOccluded(mainPos5, ball.position, allBalls)) {
          candidates.push(new FreeKickPosition(mainPos5, ball.position, hole));
        }
        const mainPos6 = new Point(ball.position.x + dl + Math.random() * 4, ball.position.y - dl - Math.random() * 4);
        if (!AiGTool.hasOccluded(mainPos6, ball.position, allBalls)) {
          candidates.push(new FreeKickPosition(mainPos6, ball.position, hole));
        }
        const mainPos7 = new Point(ball.position.x - dl - Math.random() * 4, ball.position.y + dl + Math.random() * 4);
        if (!AiGTool.hasOccluded(mainPos7, ball.position, allBalls)) {
          candidates.push(new FreeKickPosition(mainPos7, ball.position, hole));
        }
        const mainPos8 = new Point(ball.position.x - dl - Math.random() * 4, ball.position.y - dl - Math.random() * 4);
        if (!AiGTool.hasOccluded(mainPos8, ball.position, allBalls)) {
          candidates.push(new FreeKickPosition(mainPos8, ball.position, hole));
        }

        const mainPos9 = new Point(ball.position.x + (Math.random() - 0.5) * 6, ball.position.y + (Math.random() - 0.5) * 6);
        if (!AiGTool.hasOccluded(mainPos9, ball.position, allBalls)) {
          candidates.push(new FreeKickPosition(mainPos9, ball.position, hole));
        }

        const mainPos10 = new Point(ball.position.x + (Math.random() - 0.5) * 6, ball.position.y + (Math.random() - 0.5) * 6);
        if (!AiGTool.hasOccluded(mainPos10, ball.position, allBalls)) {
          candidates.push(new FreeKickPosition(mainPos10, ball.position, hole));
        }

        const mainPos11 = new Point(ball.position.x + (Math.random() - 0.5) * 6, ball.position.y + (Math.random() - 0.5) * 6);
        if (!AiGTool.hasOccluded(mainPos11, ball.position, allBalls)) {
          candidates.push(new FreeKickPosition(mainPos11, ball.position, hole));
        }

        const mainPos12 = new Point(ball.position.x + (Math.random() - 0.5) * 6, ball.position.y + (Math.random() - 0.5) * 6);
        if (!AiGTool.hasOccluded(mainPos12, ball.position, allBalls)) {
          candidates.push(new FreeKickPosition(mainPos12, ball.position, hole));
        }
      }
    }
    return candidates;
  }
}

